#include <arch/interrupts.h>
#include <arch/idt.h>
#include <arch/vectors.h>
#include <arch/io.h>
#include <process.h>
#include <log.h>
#include <hal/apic.h>

static isr funcs[256];

extern int check_idt_exist(u32 vector);

void intr_register_isr(const u8 vector, isr func) {
    if (!check_idt_exist(vector)) {
        panic("IDT vector %d not exist\n", vector);
    }
    funcs[vector] = func;
}

void intr_delete_isr(const u8 vector, isr func) {
    if (funcs[vector] == func) {
        funcs[vector] = 0;
    }
}
void interrupts_handler(isr_param* p) {
    cur_proc->context = *p;

    isr_param* param = &cur_proc->context;
    isr func = funcs[param->ctx->vector];

    if (func) {
        func(p);
    } else {
        panic("isr %d invalid, code 0x%X, eip 0x%X, esp 0x%X\n", param->ctx->vector, param->ctx->err_code, param->ctx->ctx.eip, param->esp);
    }
    if (param->ctx->vector >= EX_INTERRUPT_BEGIN && param->ctx->vector != APIC_SPIV_IV) {
        apic_done_service();
    }
}

void DIVISION_ERROR(const isr_param* param) {
    panic("DIVISION_ERROR\n");
}
void GENERAL_PROTECTION_FAULT(const isr_param* param) {
    u32 err_code = param->ctx->err_code & 0xFFFF;
    u32 external = err_code & 0x01;
    u32 tbl = (err_code >> 1) & 0x03;
    u32 idx = err_code >> 3;
    const char table[][4] = { "GDT", "IDT", "LDT", "IDT" };
    const char source[][9] = { "Internal", "External" };
    panic("GPF code:0x%X %s %s: %d\n", err_code, source[external], table[tbl], idx);
}
void X87_FAULT(const isr_param* param) {
    panic("X87_FAULT error code %d, eip 0x%X, esp 0x%X\n", param->ctx->err_code, param->ctx->ctx.eip, param->esp);
}
void apic_error(const isr_param* param) {
    u32 reg = apic_read(APIC_ESR);
    panic("APIC_ERROR: %X\n", reg);
}

extern void syscall_handler();

void register_isr() {
    intr_register_isr(FAULT_DIVISION_ERROR, &DIVISION_ERROR);
    intr_register_isr(FAULT_GENERAL_PROTECTION, &GENERAL_PROTECTION_FAULT);
    intr_register_isr(FAULT_X87_FAULT, &X87_FAULT);
    intr_register_isr(0x80, &syscall_handler);
    intr_register_isr(APIC_ERROR_IV, &apic_error);
}

/* 初始化可编程中断控制器8259A */
static void init_pic() {
    /* 初始化主片 */
    outb(PIC_M_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4.
    outb(PIC_M_DATA, 0x20); // ICW2: 起始中断向量号为0x20,也就是IR[0-7] 为 0x20 ~ 0x27.
    outb(PIC_M_DATA, 0x04); // ICW3: IR2接从片.
    outb(PIC_M_DATA, 0x01); // ICW4: 8086模式, 正常EOI

    /* 初始化从片 */
    outb(PIC_S_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4.
    outb(PIC_S_DATA, 0x28); // ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F.
    outb(PIC_S_DATA, 0x02); // ICW3: 设置从片连接到主片的IR2引脚
    outb(PIC_S_DATA, 0x01); // ICW4: 8086模式, 正常EOI

    /* IRQ2用于级联从片,必须打开,否则无法响应从片上的中断
    主片上打开的中断有IRQ0的时钟,IRQ1的键盘和级联从片的IRQ2,其它全部关闭 */
    outb(PIC_M_DATA, 0xff);

    /* 打开从片上的IRQ14,此引脚接收硬盘控制器的中断 */
    outb(PIC_S_DATA, 0xbf);
}

void init_interrupts() {
    init_idt();
    register_isr();
    init_pic();
}